<template>
  <div class="generate-ppt-container">
    <a-row :gutter="24">
      <a-col :span="12">
        <!-- 左侧生成方式选择 -->
        <a-card class="section-card">
          <template #title>
            <span class="card-title">PPT生成方式</span>
          </template>

          <a-tabs v-model:activeKey="activeTab" @change="handleTabChange">
            <a-tab-pane key="outline" tab="根据大纲生成">
              <div class="tab-content">
                <a-form :model="exceptUploadState" layout="vertical">
                  <a-form-item label="PPT主题">
                    <a-input
                      v-model:value="selectedTemplateView"
                      placeholder="请双击右侧PPT模板即可选择主题"
                    />
                  </a-form-item>
                </a-form>
                <!--label="课程大纲"-->
                <a-form-item>
                  <a-textarea
                    v-model:value="exceptUploadState.query"
                    placeholder="请输入课程大纲内容..."
                    :rows="18"
                    show-count
                    :maxlength="5000"
                  />
                </a-form-item>
              </div>
            </a-tab-pane>

            <a-tab-pane key="upload" tab="根据文件生成">
              <div class="tab-content">
                <a-form :model="uploadForm" layout="vertical">
                  <a-form-item label="PPT主题">
                    <a-input
                      v-model:value="selectedTemplateView"
                      placeholder="请双击右侧PPT模板即可选择主题"
                    />
                  </a-form-item>
                  <a-form-item label="上传文件">
                    <a-upload-dragger
                      name="file"
                      :multiple="false"
                      :before-upload="beforeUpload"
                      :custom-request="handleUpload"
                      :show-upload-list="false"
                    >
                      <p class="ant-upload-drag-icon">
                        <inbox-outlined />
                      </p>
                      <p class="ant-upload-text">点击或拖拽文件到此区域上传</p>
                      <p class="ant-upload-hint">
                        支持 PDF、DOC、DOCX、TXT、MD 格式（不支持扫描件）
                      </p>
                    </a-upload-dragger>
                  </a-form-item>
                </a-form>
              </div>
            </a-tab-pane>

            <a-tab-pane key="url" tab="根据URL生成">
              <div class="tab-content">
                <a-form :model="exceptUploadState" layout="vertical">
                  <a-form-item label="PPT主题">
                    <a-input
                      v-model:value="selectedTemplateView"
                      placeholder="请双击右侧PPT模板即可选择主题"
                    />
                  </a-form-item>
                  <a-form-item label="文件URL">
                    <a-input v-model:value="exceptUploadState.fileUrl" placeholder="请输入文件URL地址" />
                  </a-form-item>
                </a-form>
              </div>
            </a-tab-pane>
          </a-tabs>
        </a-card>

        <!-- 生成按钮 -->
        <div class="generate-section">
          <a-button type="primary" size="large" block :loading="generating" @click="handleGenerate">
            <template #icon>
              <rocket-outlined />
            </template>
            生成PPT
          </a-button>
        </div>
      </a-col>

      <a-col :span="12">
        <!-- 右侧模板选择 -->
        <a-card class="section-card">
          <template #title>
            <span class="card-title">模板选择</span>
          </template>

          <!-- 模板筛选 -->
          <div class="template-filters">
            <div class="filter-header">
              <span class="template-count">共 {{ total }} 个模板</span>
            </div>
            <a-row :gutter="12">
              <a-col :span="8">
                <a-form-item label="风格">
                  <a-select
                    v-model:value="searchParams.style"
                    placeholder="选择风格"
                    allowClear
                    @change="handleFilterChange"
                  >
                    <a-select-option v-for="style in styleOptions" :key="style" :value="style">
                      {{ style }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </a-col>
              <a-col :span="8">
                <a-form-item label="颜色类型">
                  <a-select
                    v-model:value="searchParams.color"
                    placeholder="选择颜色"
                    allowClear
                    @change="handleFilterChange"
                  >
                    <a-select-option v-for="color in colorOptions" :key="color" :value="color">
                      {{ color }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </a-col>
              <a-col :span="8">
                <a-form-item label="行业类型">
                  <a-select
                    v-model:value="searchParams.industry"
                    placeholder="选择行业"
                    allowClear
                    @change="handleFilterChange"
                  >
                    <a-select-option
                      v-for="industry in industryOptions"
                      :key="industry"
                      :value="industry"
                    >
                      {{ industry }}
                    </a-select-option>
                  </a-select>
                </a-form-item>
              </a-col>
            </a-row>
          </div>

          <!-- 模板展示 -->
          <div class="template-grid">
            <a-row :gutter="[12, 12]" v-if="templateList.length > 0">
              <a-col :span="8" v-for="template in templateList" :key="template.templateIndexId">
                <div
                  class="template-item"
                  :class="{ active: selectedTemplate === template.templateIndexId }"
                  @dblclick="handleTemplateDblClick(template)"
                >
                  <a-carousel autoplay :dots="false" arrows>
                    <template #prevArrow>
                      <div class="custom-slick-arrow" style="left: 5px; z-index: 1">
                        <left-circle-outlined />
                      </div>
                    </template>
                    <template #nextArrow>
                      <div class="custom-slick-arrow" style="right: 5px">
                        <right-circle-outlined />
                      </div>
                    </template>
                    <div v-for="(image, index) in template.imageList" :key="index">
                      <img
                        :src="image"
                        :alt="`${template.industry} - ${index + 1}`"
                        @error="handleImageError"
                        @load="handleImageLoad"
                      />
                    </div>
                  </a-carousel>
                  <div class="template-info">
                    <div class="template-name">{{ template.industry }}</div>
                    <div class="template-tags">
                      <a-tag size="small" :color="getTagColor(template?.style || '')">{{
                        template.style
                      }}</a-tag>
                      <a-tag size="small" :color="getTagColor(template?.style || '')">{{
                        template.color
                      }}</a-tag>
                    </div>
                  </div>
                </div>
              </a-col>
            </a-row>

            <!-- 空状态 -->
            <div v-else class="empty-state">
              <div class="empty-icon">
                <svg
                  width="64"
                  height="64"
                  viewBox="0 0 24 24"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.89 22 5.99 22H18C19.1 22 20 21.1 20 20V8L14 2Z"
                    stroke="#D9D9D9"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M14 2V8H20"
                    stroke="#D9D9D9"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M16 13H8"
                    stroke="#D9D9D9"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M16 17H8"
                    stroke="#D9D9D9"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M10 9H9H8"
                    stroke="#D9D9D9"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                </svg>
              </div>
              <div class="empty-text">
                <h3>暂无匹配的模板</h3>
                <p>请尝试调整筛选条件或选择其他风格、颜色、行业类型</p>
              </div>
              <a-button type="primary" @click="clearFilters"> 清除筛选条件 </a-button>
            </div>
          </div>
          <!-- 页数选择 -->
          <div class="page-count-section">
            <a-pagination
              v-model:current="current"
              :total="Number(total)"
              show-less-items
              :show-total="(total: Number) => `共有 ${total}个模板`"
              @change="handlePageChange"
            />
          </div>
        </a-card>
      </a-col>
    </a-row>

    <!-- 左侧触发器按钮 -->
    <div class="trigger-btn" @click="drawerOpen = true">
      <span>&gt;</span>
    </div>
    <!-- 遮罩层 -->
    <div v-if="drawerOpen" class="drawer-mask" @click="drawerOpen = false"></div>
    <!-- 自定义毛玻璃抽屉 -->
    <div class="custom-drawer" :class="{ open: drawerOpen }">
      <div class="drawer-content" @click.stop>
        <div class="drawer-close" @click="drawerOpen = false">&times;</div>
        <div class="drawer-form">
          <div class="drawer-title">PPT生成历史记录</div>
          <div class="drawer-pane drawer-ppt-list">
            <ul>
              <li
                v-for="record in historyDataList"
                :key="record.id"
                @dblclick="handleSwitchPPT(record)"
              >
                <div class="ppt-cover">
                  <img
                    :src="record.coverImgSrc"
                    :alt="record.title"
                    @error="handleCoverImageError"
                    @load="handleCoverImageLoad"
                  />
                </div>
                <div class="ppt-content">
                  <div class="ppt-title">{{ record.title }}</div>
                  <div class="ppt-info">
                    <!--                  <span class="ppt-template">{{ record.templateName }}</span>-->
                    <span class="ppt-date">{{ formatDate(record?.publishTime || '') }}</span>
                  </div>
                  <div class="ppt-actions">
                    <div class="downloadBtn">
                      <a-button
                        size="small"
                        :disabled="record.status !== 1"
                        @click.stop="handleDownload(record)"
                      >
                        下载
                      </a-button>
                    </div>
                    <div class="previewBtn">
                      <a-button
                        size="small"
                        :disabled="record.status !== 1"
                        @click.stop="handlePreview(record)"
                      >
                        预览
                      </a-button>
                    </div>
                  </div>
                  <div class="ppt-status" :class="getStatusEN(record?.status || 1)">
                    {{ getStatusText(record?.status || 1) }}
                  </div>
                </div>
              </li>
            </ul>
          </div>

          <!-- 分页组件 -->
          <div class="drawer-pagination">
            <a-pagination
              v-model:current="queryParams.current"
              :total="Number(historyTotal)"
              :page-size="queryParams.pageSize"
              show-less-items
              size="small"
              :show-total="(total: Number) => `共${total}条数据`"
              :hideOnSinglePage="true"
              @change="handlePPTPageChange"
            />
          </div>
        </div>
      </div>
    </div>
  </div>

  <a-modal v-model:open="modalOpen" :footer="null" width="520" centered :closable="false">
    <template v-if="generating">
      <div class="progress-container">
        <div class="progress-header">
          <div class="progress-title">PPT 生成中</div>
          <div class="progress-percentage">{{ currentProgress }}%</div>
        </div>
        
        <div class="progress-bar-container">
          <div class="progress-bar">
            <div 
              class="progress-fill" 
              :style="{ width: currentProgress + '%' }"
            ></div>
          </div>
        </div>
        
        <div class="progress-stage">{{ currentStage }}</div>
        
        <div class="progress-tips">
          <div class="tip-item">
            <span class="tip-dot"></span>
            <span>正在为您精心制作PPT...</span>
          </div>
          <div class="tip-item">
            <span class="tip-dot"></span>
            <span>预计需要1-3分钟，请稍候</span>
          </div>
        </div>
      </div>
    </template>
    <template v-else-if="pptUrl">
      <div style="text-align: center">
        <div style="margin-bottom: 16px">PPT生成成功！</div>
        <a :href="pptUrl" target="_blank" download>
          <a-button type="primary">点击下载PPT</a-button>
        </a>
      </div>
    </template>
  </a-modal>
  <DocumentPreview ref="previewRef" />
</template>

<script setup lang="ts">
import { ref, reactive, onMounted, watch, onBeforeUnmount } from 'vue'
import { message } from 'ant-design-vue'
import {
  InboxOutlined,
  RocketOutlined,
  LeftCircleOutlined,
  RightCircleOutlined,
} from '@ant-design/icons-vue'
import {
  checkProgressAsync, checkProgressImmediately,
  generatePpt, generatePptOutlineByFileUrl,
  generatePptOutlineByUploadFile, generatePptOutlineByUploadOutline,
  getTemplateList,
  listGeneratePptVoByPage
} from '@/api/generatePptController'
import DocumentPreview from '@/components/common/preview/DocumentPreview.vue'
//模板的当前页数
const current = ref(1)
// 当前激活的标签页
const activeTab = ref('outline')
//模态框的控制
const modalOpen = ref(false)
//是否生成中
const generating = ref(false)
//ppt的url
const pptUrl = ref<string>()

// 进度状态管理
const currentProgress = ref<number>(0) // 当前总进度 0-100
const currentStage = ref<string>('') // 当前阶段描述
const isGenerating = ref(false) // 是否正在生成
const progressTimer = ref<number | null>(null) // 进度轮询定时器

// 监听 modal 关闭时清理轮询
watch(modalOpen, (val) => {
  if (!val && progressTimer.value) {
    window.clearInterval(progressTimer.value)
    progressTimer.value = null
  }
})

// 组件卸载时清理轮询
onBeforeUnmount(() => {
  if (progressTimer.value) {
    window.clearInterval(progressTimer.value)
    progressTimer.value = null
  }
})

// 抽屉展示相关
const drawerOpen = ref(false)
/**
 * 历史数据相关
 */
const historyTotal = ref(0)
// 历史数据分页查询
const queryParams = reactive<API.GeneratePptQueryRequest>({
  current: 1,
  pageSize: 4,
})

const historyDataList = ref<API.GeneratePptVO[]>([])
const fetchHistoryData = async () => {
  try {
    const res = await listGeneratePptVoByPage({
      ...queryParams,
    })
    if (res.data.data && res.data.code === 0) {
      historyDataList.value = res.data.data.records ?? []
      historyTotal.value = Number(res.data.data.total)
    } else {
      message.error('获取历史数据失败')
    }
  } catch (e) {
    message.error('获取历史数据失败')
  }
}

// 格式化日期
const formatDate = (dateStr: string) => {
  const date = new Date(dateStr)
  return date.toLocaleDateString('zh-CN', {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  })
}
// 获取状态英文文本
const getStatusEN = (status: number) => {
  const statusMap: Record<string, string> = {
    1: 'completed',
    5: 'processing',
    6: 'failed',
  }
  return statusMap[status] || '未知状态'
}
// 获取状态文本
const getStatusText = (status: number) => {
  const statusMap: Record<string, string> = {
    1: '已完成',
    5: '生成中',
    6: '生成失败',
  }
  return statusMap[status] || '未知状态'
}

// 下载历史记录中的PPT
const handleDownload = (record: API.GeneratePptVO) => {
  if (record.status !== 1 || !record.pptUrl) {
    message.info('PPT未完成，暂不可下载')
    return
  }
  window.open(record.pptUrl, '_blank')
  message.success('正在下载PPT文件')
  drawerOpen.value = false
}

// 预览历史记录中的PPT
const previewRef = ref<InstanceType<typeof DocumentPreview> | null>(null)
const handlePreview = (record: API.GeneratePptVO) => {
  if (record.status !== 1 || !record.pptUrl) {
    message.info('PPT未完成，暂不可预览')
    return
  }
  // 使用通用文档预览组件
  previewRef.value?.open(record.pptUrl)
  drawerOpen.value = false
}

// 双击切换PPT
const handleSwitchPPT = (record: any) => {
  if (record.status === 1 && record.pptUrl) {
    // 如果是已完成的PPT，直接下载
    window.open(record.pptUrl, '_blank')
    message.success('正在下载PPT文件')
  } else if (record.status === 5) {
    message.info('PPT正在生成中，请稍候...')
  } else {
    message.error('PPT生成失败，无法下载')
  }
  drawerOpen.value = false
}

// 处理PPT历史记录分页
const handlePPTPageChange = (page: any) => {
  queryParams.current = page
  queryParams.pageSize = 4
  fetchHistoryData()
}

/**
 * 模板查询相关
 */
const total = ref(0)
//模板查询参数
const searchParams = reactive<API.ThemeTemplateQueryRequest>({
  pageNum: 1,
  pageSize: 9,
})
//模板列表
const templateList = ref<API.ThemeRecord[]>([])
//加载模板
const fetchTemplate = async () => {
  try {
    const res = await getTemplateList({
      ...searchParams,
    })
    if (res.data.data && res.data.code === 0) {
      templateList.value = res.data.data.records || []
      total.value = res.data.data.total ?? 0
    } else {
      message.error('获取模板失败')
    }
  } catch (e) {
    message.error('获取模板失败')
  }
}
//获取数据
onMounted(() => {
  fetchTemplate() //获取模板列表
  fetchHistoryData() //获取历史数据
})
/**
 * ppt生成相关
 */
const uploadForm = reactive({
  file: null as any,
})
const exceptUploadState = reactive<API.GeneratePPTRequest>({
  query:'',
  fileUrl:''
})
// 选中的模板
const selectedTemplate = ref<API.ThemeRecord>()
//选中的模板id
const selectedTemplateView = ref<string>('')
// 选项数据
const styleOptions = ['简约', '卡通', '商务', '创意', '国风', '清新', '扁平', '插画', '节日']

const colorOptions = ['蓝色', '绿色', '红色', '紫色', '黑色', '灰色', '黄色', '粉色', '橙色']

const industryOptions = [
  '科技互联网',
  '教育培训',
  '政务',
  '学院',
  '电子商务',
  '金融战略',
  '法律',
  '医疗健康',
  '文旅体育',
  '艺术广告',
  '人力资源',
  '游戏娱乐',
]

// 事件处理
const handleTabChange = (key: string) => {
  activeTab.value = key
}

const handleFilterChange = () => {
  searchParams.pageNum = 1
  fetchTemplate()
}
// 处理表格变更（排序、筛选等）
const handlePageChange = (page: any, filters: any, sorter: any) => {
  searchParams.pageNum = current.value
  fetchTemplate()
}

const clearFilters = () => {
  searchParams.style = undefined
  searchParams.color = undefined
  searchParams.industry = undefined
  current.value = 1 // 重置到第一页
}

const handleImageError = (event: Event) => {
  const img = event.target as HTMLImageElement
  img.src =
    ''
}

const handleImageLoad = (event: Event) => {
  // 图片加载成功，可以在这里添加加载完成的逻辑
  const img = event.target as HTMLImageElement
  img.style.opacity = '1'
}

const handleCoverImageError = (event: Event) => {
  const img = event.target as HTMLImageElement
  img.src =
    ''
}

const handleCoverImageLoad = (event: Event) => {
  const img = event.target as HTMLImageElement
  img.style.opacity = '1'
}
const handleTemplateDblClick = (template: API.ThemeRecord) => {
  selectedTemplate.value = template
  //展示给用户的为： 行业 + 风格 + 颜色
  selectedTemplateView.value = template.industry + ' ' + template.style + ' ' + template.color
}

const beforeUpload = (file: File) => {
  const isValidType = /\.(pdf|doc|docx|txt|md)$/i.test(file.name)
  if (!isValidType) {
    message.error('只支持 PDF、DOC、DOCX、TXT、MD 格式文件！')
    return false
  }

  const isLt10M = file.size / 1024 / 1024 < 10
  if (!isLt10M) {
    message.error('文件大小不能超过 10MB！')
    return false
  }

  return isValidType && isLt10M // 阻止自动上传，使用自定义上传
}
//上传后的pptId
const currentPptId = ref<string>('')
//处理上传
const handleUpload = async ({ file }: any) => {
  //pptUrl
  // 保存上传的文件信息
  generating.value = true
  const res = await generatePptOutlineByUploadFile(file)
  try {
    if (res.data.data && res.data.code === 0) {
      message.success('文件上传成功')
      currentPptId.value = res.data.data
      generating.value = false
    } else {
      message.error('文件上传失败,' + res.data.msg)
    }
  } catch (e) {
    message.error('文件上传失败,' + res.data.msg)
  } finally {
    generating.value = false
  }
}

const askProgress = async (sid: string) => {
  try {
    const res = await checkProgressAsync({
      sid: sid,
    })
    if (res.data.code === 0) {
      if (res.data.data) {
        pptUrl.value = res.data.data
        generating.value = false
        modalOpen.value = false
        // 使用通用文档预览组件
        previewRef.value?.open(pptUrl.value)
        message.success('ppt生成成功')
      }
    } else {
      message.error('ppt生成失败')
      generating.value = false
    }
  } finally {
  }
}
const imProgress = ref<API.CheckProgressVO>()

// 新的进度轮询：将 finishRatio (0-100 或 0-1) 映射到剩余 60% 区间
const pollProgress = async (sid: string) => {
  // 防抖保护
  if (!sid) return
  currentStage.value = '正在根据大纲生成PPT...'
  // 清理旧定时器
  if (progressTimer.value) {
    window.clearInterval(progressTimer.value)
    progressTimer.value = null
  }
  // 采用定时轮询
  progressTimer.value = window.setInterval(async () => {
    try {
      const res = await checkProgressImmediately({ sid })
      if (res.data.code !== 0) return
      const data = res.data.data
      if (!data) return
      imProgress.value = data
      const raw = Number(data.finishRatio ?? 0)
      const ratio = raw > 1 ? raw : raw * 100 // 兼容 0-1 或 0-100
      const mapped = Math.min(100, Math.max(40, Math.floor(40 + (ratio * 0.6))))
      currentProgress.value = mapped
      if (data.status === 'done') {
        pptUrl.value = data.pptUrl
        currentProgress.value = 100
        generating.value = false
        modalOpen.value = false
        if (progressTimer.value) {
          window.clearInterval(progressTimer.value)
          progressTimer.value = null
        }
        if (pptUrl.value) {
          previewRef.value?.open(pptUrl.value)
          message.success('PPT生成成功！')
        } else {
          message.success('PPT生成完成，但未返回文件地址')
        }
      }
    } catch (e) {
      // 不中断用户流程，仅提示一次
    }
  }, 1200)
}

// 第一阶段模拟：生成大纲，占 40%
const simulateOutlineProgress = async (text: string) => {
  currentStage.value = text
  // 保证从 0 往 40% 缓动
  currentProgress.value = Math.max(currentProgress.value, 5)
  const target = 40
  while (currentProgress.value < target) {
    await new Promise((r) => setTimeout(r, 200))
    currentProgress.value = Math.min(target, currentProgress.value + Math.ceil(Math.random() * 5))
  }
}

// 封装生成 PPT 的共用流程
const startGeneratePpt = async () => {
  generating.value = true
  modalOpen.value = true
  // 进入第二阶段
  await new Promise((r) => setTimeout(r, 300))
  currentStage.value = '正在提交任务并生成PPT...'
}
//生成ppt
/**
 * 根据 activeTab 的类型选择对应接口api  outline/upload/url
 */
const handleGenerate = async () => {

  if (!selectedTemplate.value) {
    message.warning('请选择一个模板')
    return
  }
  switch (activeTab.value) {
    case "outline": {
      await handleGenerateByOutline()
    }
      break;
    case "upload" : {
      await handleGenerateByUpload()
    }
      break;
    case "url": {
      await handleGenerateByUrl()
    }
      break;
    default: {
      message.error("请您选择一个方式来生成PPT")
      throw new Error("Invalid tab selected");
    }
  }
}
/**
 * 通过大纲生成ppt
 */
const handleGenerateByOutline = async ()=>{
  // 重置并开启进度展示 - 第一阶段模拟到40%
  currentProgress.value = 0
  generating.value = true
  modalOpen.value = true
  try {
    // 并行执行：模拟进度 + 生成大纲
    const simulatePromise = simulateOutlineProgress('系统正在根据您提供的参考材料为您生成PPT大纲，请稍候...')
    const resp = await generatePptOutlineByUploadOutline({
      ...exceptUploadState
    })
    await simulatePromise // 确保模拟进度完成
    
    if (resp.data.data && resp.data.code === 0) {
      currentPptId.value = resp.data.data
      // 进入第二阶段：根据大纲生成PPT
      await startGeneratePpt()
      const res = await generatePpt({
        id: currentPptId.value,
        templateId: selectedTemplate.value?.templateIndexId,
        coverImgSrc: selectedTemplate.value?.imageList[0]
      })
      if (res.data.data && res.data.code === 0) {
        message.success('PPT生成中！')
        const sid = res.data.data
        // 实时轮询进度（映射到40%-100%）
        pollProgress(sid)
      } else {
        message.error('PPT生成失败，请重试')
        generating.value = false
      }
    } else {
      message.error('生成大纲失败，请稍后重试')
      generating.value = false
    }
  } catch (e) {
    console.error(e)
    message.error('PPT生成失败，请重试')
    generating.value = false
  }
}
/**
 * 通过文件生成ppt
 */
const handleGenerateByUpload = async ()=>{
  // 根据文件生成：直接进入第二阶段，从40%开始
  generating.value = true
  modalOpen.value = true
  currentProgress.value = 40
  try {
    await startGeneratePpt()
    currentStage.value = '正在根据大纲生成PPT...'
    const res = await generatePpt({
      id: currentPptId.value,
      // id: '1947583876551856130',
      templateId: selectedTemplate.value?.templateIndexId,
      coverImgSrc: selectedTemplate.value?.imageList[0]
    })
    if (res.data.data && res.data.code === 0) {
      message.success('PPT生成中！')
      const sid = res.data.data
      // 实时轮询进度（映射到40%-100%）
      pollProgress(sid)
    } else {
      message.error('PPT生成失败，请重试')
      generating.value = false
    }
  } catch (e) {
    message.error('PPT生成失败，请重试')
    generating.value = false
  }
}
/**
 * 通过文件url生成ppt
 */
const handleGenerateByUrl = async ()=>{
  // 重置并开启进度展示 - 第一阶段模拟到40%
  currentProgress.value = 0
  generating.value = true
  modalOpen.value = true
  try {
    const simulatePromise = simulateOutlineProgress('系统正在根据您提供的URL解析并生成PPT大纲，请稍候...')
    const resp = await generatePptOutlineByFileUrl({
      ...exceptUploadState
    })
    await simulatePromise
    if (resp.data.data && resp.data.code === 0) {
      currentPptId.value = resp.data.data
      // 第二阶段
      await startGeneratePpt()
      const res = await generatePpt({
        id: currentPptId.value,
        templateId: selectedTemplate.value?.templateIndexId,
        coverImgSrc: selectedTemplate.value?.imageList[0]
      })
      if (res.data.data && res.data.code === 0) {
        message.success('PPT生成中！')
        const sid = res.data.data
        // 轮询第二阶段进度
        pollProgress(sid)
      } else {
        message.error('PPT生成失败，请重试')
        generating.value = false
      }
    } else {
      message.error('生成大纲失败，请稍后重试')
      generating.value = false
    }
  } catch (e) {
    console.error(e)
    message.error('PPT生成失败，请重试')
    generating.value = false
  }
}
// 获取标签颜色
const getTagColor = (text: string) => {
  const colorMap: Record<string, string> = {
    简约: 'blue',
    卡通: 'green',
    商务: 'purple',
    创意: 'orange',
    国风: 'red',
    清新: 'cyan',
    扁平: 'geekblue',
    插画: 'magenta',
    节日: 'gold',
    蓝色: 'blue',
    绿色: 'green',
    红色: 'red',
    紫色: 'purple',
    黑色: 'default',
    灰色: 'default',
    黄色: 'gold',
    粉色: 'pink',
    橙色: 'orange',
  }
  return colorMap[text] || 'default'
}
</script>

<style scoped>
.generate-ppt-container {
  padding: 24px;
  /*  background: url('@/assets/BackGround/pptbg.png') no-repeat center center;*/
  background-size: cover;
  min-height: 100vh;
  position: relative;
  animation: fade-in-page 0.8s cubic-bezier(0.23, 1, 0.32, 1);
}

.generate-ppt-container::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(255, 255, 255, 0.85);
  /*  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));*/
  z-index: 0;
}

@keyframes fade-in-page {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes slideInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideInDown {
  from {
    opacity: 0;
    transform: translateY(-30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideInLeft {
  from {
    opacity: 0;
    transform: translateX(-30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideInRight {
  from {
    opacity: 0;
    transform: translateX(30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes float {
  0%,
  100% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-10px);
  }
}

@keyframes shimmer {
  0% {
    background-position: -200px 0;
  }
  100% {
    background-position: calc(200px + 100%) 0;
  }
}

.section-card {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 20px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  opacity: 0;
  animation: card-fade-in 0.8s cubic-bezier(0.23, 1, 0.32, 1) forwards;
  backdrop-filter: blur(15px);
  z-index: 1;
}

.section-card:hover {
  box-shadow: 0 16px 48px rgba(102, 126, 234, 0.25);
  transform: translateY(-8px) scale(1.02);
  background: rgba(255, 255, 255, 0.98);
}

@keyframes card-fade-in {
  to {
    opacity: 1;
    transform: none;
  }
}

.card-title {
  font-size: 20px;
  font-weight: 700;
  color: #2c3e50;
  background: linear-gradient(135deg, #667eea, #764ba2);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.tab-content {
  padding: 20px 0;
  animation: content-fade-in 0.6s ease-out;
}

@keyframes content-fade-in {
  from {
    opacity: 0;
    transform: translateX(20px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

:deep(.ant-tabs-tab) {
  transition: all 0.3s ease;
  border-radius: 8px 8px 0 0;
}

:deep(.ant-tabs-tab:hover) {
  color: #fff;
}

:deep(.ant-tabs-tab-active) {
  color: #fff;
}

:deep(.ant-input),
:deep(.ant-input-affix-wrapper) {
  border-radius: 12px;
  border: 2px solid #e8e8e8;
  transition: all 0.3s ease;
}

:deep(.ant-input:focus),
:deep(.ant-input-affix-wrapper:focus) {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  transform: translateY(-2px);
}

:deep(.ant-textarea) {
  border-radius: 12px;
  border: 2px solid #e8e8e8;
  transition: all 0.3s ease;
}

:deep(.ant-textarea:focus) {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

.template-filters {
  margin-bottom: 24px;
  padding-bottom: 20px;
  border-bottom: 2px solid rgba(102, 126, 234, 0.2);
  background: linear-gradient(135deg, rgba(248, 249, 255, 0.9), rgba(240, 242, 255, 0.8));
  backdrop-filter: blur(10px);
  border-radius: 16px;
  padding: 20px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  animation: slideInUp 0.8s ease-out 0.6s both;
  z-index: 1;
}

.filter-header {
  margin-bottom: 16px;
  text-align: center;
}

.template-count {
  font-size: 14px;
  font-weight: 600;
  color: #2c3e50;
  background: linear-gradient(135deg, #667eea, #764ba2);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.template-filters .ant-form-item {
  margin-bottom: 0;
}

:deep(.ant-select) {
  border-radius: 8px;
}

:deep(.ant-select-focused .ant-select-selector) {
  border-color: #667eea !important;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
}

.template-grid {
  margin-bottom: 24px;
}

.template-item {
  border: 3px solid rgba(102, 126, 234, 0.2);
  border-radius: 16px;
  overflow: hidden;
  cursor: pointer;
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(8px);
  position: relative;
  opacity: 0;
  animation:
    template-fade-in 0.6s ease-out forwards,
    float 3s ease-in-out infinite;
  height: 160px;
  display: flex;
  flex-direction: column;
  z-index: 1;
}

.template-item:nth-child(1) {
  animation-delay: 0.1s;
}
.template-item:nth-child(2) {
  animation-delay: 0.2s;
}
.template-item:nth-child(3) {
  animation-delay: 0.3s;
}
.template-item:nth-child(4) {
  animation-delay: 0.4s;
}
.template-item:nth-child(5) {
  animation-delay: 0.5s;
}
.template-item:nth-child(6) {
  animation-delay: 0.6s;
}
.template-item:nth-child(7) {
  animation-delay: 0.7s;
}
.template-item:nth-child(8) {
  animation-delay: 0.8s;
}
.template-item:nth-child(9) {
  animation-delay: 0.9s;
}

/* 分页切换动画 */
.template-grid {
  position: relative;
}

.template-grid .ant-row {
  transition: opacity 0.3s ease;
}

.template-grid .ant-row.page-transition {
  opacity: 0;
}

@keyframes template-fade-in {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.8);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.template-item:hover {
  border-color: #667eea;
  transform: translateY(-8px) scale(1.05);
  box-shadow: 0 12px 32px rgba(102, 126, 234, 0.25);
}

.template-item::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 1;
}

.template-item:hover::before {
  opacity: 1;
}

.template-item img {
  width: 100%;
  height: 160px;
  object-fit: cover;
  transition: all 0.3s ease;
  opacity: 0;
  animation: image-fade-in 0.5s ease forwards;
}

@keyframes image-fade-in {
  to {
    opacity: 1;
  }
}

.template-item:hover img {
  transform: scale(1.1);
}

.template-info {
  padding: 8px;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.85), rgba(248, 249, 255, 0.85));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid rgba(102, 126, 234, 0.15);
  opacity: 0;
  transform: translateY(100%);
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  pointer-events: none;
  max-height: 60px;
  overflow: hidden;
  visibility: hidden;
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
}

.template-item:hover .template-info {
  opacity: 1;
  transform: translateY(0);
  max-height: 60px;
  visibility: visible;
  box-shadow: 0 -4px 25px rgba(0, 0, 0, 0.15);
}

/* 确保非hover状态下隐藏信息区域 */
.template-item:not(:hover) .template-info {
  opacity: 0 !important;
  transform: translateY(100%) !important;
  visibility: hidden !important;
}

.template-name {
  font-size: 11px;
  font-weight: 600;
  margin-bottom: 4px;
  color: #2c3e50;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transform: translateY(5px);
  transition: all 0.3s ease 0.05s;
  line-height: 1.2;
}

.template-item:hover .template-name {
  opacity: 1;
  transform: translateY(0);
}

/* 确保非hover状态下隐藏 */
.template-item:not(:hover) .template-name {
  opacity: 0 !important;
  transform: translateY(5px) !important;
}

.template-tags {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
  max-height: 20px;
  overflow: hidden;
}

.template-tags .ant-tag {
  font-size: 8px;
  padding: 0 4px;
  line-height: 14px;
  border-radius: 6px;
  font-weight: 500;
  opacity: 0;
  transform: translateY(5px);
  transition: all 0.3s ease;
  flex-shrink: 0;
}

.template-tags .ant-tag:nth-child(1) {
  transition-delay: 0.1s;
}

.template-tags .ant-tag:nth-child(2) {
  transition-delay: 0.15s;
}

.template-item:hover .template-tags .ant-tag {
  opacity: 1;
  transform: translateY(0);
}

/* 确保非hover状态下隐藏标签 */
.template-item:not(:hover) .template-tags .ant-tag {
  opacity: 0 !important;
  transform: translateY(5px) !important;
}

/* 移除overlay相关样式 */

.page-count-section {
  margin-bottom: 24px;
  padding: 20px;
  background: linear-gradient(135deg, #f8f9ff, #f0f2ff);
  border-radius: 16px;
  text-align: center;
  border: 2px solid #e8e8e8;
  transition: all 0.3s ease;
}

.page-count-section:hover {
  border-color: #667eea;
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.15);
}

:deep(.ant-pagination-item) {
  border-radius: 8px;
  transition: all 0.3s ease;
}

:deep(.ant-pagination-item:hover) {
  border-color: #667eea;
  transform: translateY(-2px);
}

:deep(.ant-pagination-item-active) {
  background: linear-gradient(135deg, #667eea, #764ba2);
  border-color: #667eea;
}

.generate-section {
  padding-top: 20px;
  border-top: 2px solid #f0f0f0;
}

:deep(.ant-btn-primary) {
  background: linear-gradient(135deg, #667eea, #764ba2);
  border: none;
  border-radius: 12px;
  height: 48px;
  font-size: 16px;
  font-weight: 600;
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
  position: relative;
  overflow: hidden;
}

:deep(.ant-btn-primary::before) {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
  transition: left 0.6s;
}

:deep(.ant-btn-primary:hover) {
  background: linear-gradient(135deg, #5a6fd8, #6a4190);
  transform: translateY(-4px) scale(1.02);
  box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
}

:deep(.ant-btn-primary:hover::before) {
  left: 100%;
}

:deep(.ant-btn-primary:active) {
  transform: translateY(-2px) scale(1.01);
}

:deep(.slick-arrow.custom-slick-arrow) {
  width: 24px;
  height: 24px;
  font-size: 24px;
  color: #fff;
  background: linear-gradient(135deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.5));
  border-radius: 50%;
  transition: all 0.3s ease;
  opacity: 0.8;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
}

:deep(.slick-arrow.custom-slick-arrow:before) {
  display: none;
}

:deep(.slick-arrow.custom-slick-arrow:hover) {
  color: #fff;
  opacity: 1;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
  transform: scale(1.1);
}

.template-item:hover :deep(.slick-arrow.custom-slick-arrow) {
  opacity: 1;
}

:deep(.ant-carousel .slick-slide) {
  text-align: center;
  height: 160px;
  line-height: 160px;
  background: #fafafa;
  overflow: hidden;
  border-radius: 8px;
}

:deep(.ant-carousel .slick-slide img) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px;
}

/* 空状态样式 */
.empty-state {
  text-align: center;
  padding: 60px 20px;
  background: linear-gradient(135deg, rgba(248, 249, 255, 0.9), rgba(240, 242, 255, 0.8));
  backdrop-filter: blur(10px);
  border-radius: 16px;
  border: 2px dashed rgba(102, 126, 234, 0.3);
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  animation: slideInUp 0.8s ease-out 1.4s both;
  z-index: 1;
}

.empty-state:hover {
  border-color: #667eea;
  background: linear-gradient(135deg, rgba(240, 242, 255, 0.95), rgba(232, 235, 255, 0.9));
  transform: translateY(-4px);
  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
}

.empty-icon {
  margin-bottom: 16px;
  opacity: 0.6;
}

.empty-text h3 {
  font-size: 18px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 8px;
}

.empty-text p {
  font-size: 14px;
  color: #666;
  margin-bottom: 24px;
  line-height: 1.5;
}

.empty-state .ant-btn {
  border-radius: 8px;
  height: 36px;
  font-weight: 500;
}

/* 响应式优化 */
@media (max-width: 1200px) {
  .generate-ppt-container {
    padding: 16px;
  }

  .template-item img {
    height: 80px;
  }

  .section-card {
    border-radius: 16px;
  }
}

@media (max-width: 768px) {
  .generate-ppt-container {
    padding: 8px;
  }

  .section-card {
    border-radius: 12px;
  }

  .template-item {
    border-radius: 12px;
  }

  .template-item img {
    height: 70px;
  }
}

/* 抽屉相关样式 */
.trigger-btn {
  position: fixed;
  left: 0;
  top: 50%;
  z-index: 1002;
  width: 45px;
  height: 90px;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.8), rgba(118, 75, 162, 0.8));
  backdrop-filter: blur(15px);
  color: #fff;
  border-radius: 0 22px 22px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  font-size: 32px;
  user-select: none;
  transform: translateY(-50%);
  box-shadow: 2px 0 12px rgba(102, 126, 234, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.2);
  animation: slideInLeft 0.4s ease-out 0.8s both;
  /*  animation: slideInLeft 0.2s ease-out 0.4s both;*/
}

.trigger-btn:hover {
  left: 8px;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
  transform: translateY(-50%) scale(1.05);
  box-shadow: 4px 0 20px rgba(102, 126, 234, 0.4);
}

.drawer-mask {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(8px);
  z-index: 1001;
  transition: opacity 0.4s;
  opacity: 1;
  animation: fadeIn 0.4s ease;
}

.custom-drawer {
  position: fixed;
  top: 200px;
  left: 0;
  width: 40vw;
  max-width: 520px;
  min-height: 600px;
  max-height: 700px;
  z-index: 1002;
  background: rgba(20, 24, 40, 0.5);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 2px 0 24px rgba(0, 0, 0, 0.25);
  border-radius: 0 18px 18px 0;
  transform: translateX(-100%);
  transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  overflow: visible;
}

.custom-drawer.open {
  transform: translateX(0);
}

.drawer-content {
  width: 100%;
  min-height: 400px;
  max-height: 700px;
  overflow-y: auto;
  padding: 12px 24px 24px 24px;
  position: relative;
  box-sizing: border-box;
  background: transparent;
  border-radius: 0 18px 18px 0;
}

.drawer-close {
  position: absolute;
  right: 18px;
  top: 18px;
  font-size: 28px;
  color: #fff;
  cursor: pointer;
  z-index: 2;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.drawer-close:hover {
  opacity: 1;
}

.drawer-form {
  margin-top: 8px;
}

.drawer-title {
  font-size: 22px;
  font-weight: 700;
  color: #fff;
  margin-bottom: 18px;
  text-align: center;
  background: linear-gradient(35deg, #e666ea, #614ba2);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}

.drawer-pane {
  margin-top: 6px;
}

.drawer-ppt-list ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.drawer-ppt-list li {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  margin-bottom: 18px;
  padding: 0;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.15);
  position: relative;
  overflow: hidden;
  display: flex;
  height: 120px;
}

.drawer-ppt-list li::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
  opacity: 0;
  transition: opacity 0.3s ease;
  z-index: 1;
}

.drawer-ppt-list li:hover {
  background: rgba(102, 126, 234, 0.15);
  box-shadow: 0 8px 24px rgba(102, 126, 234, 0.2);
  transform: translateY(-4px);
  border-color: rgba(102, 126, 234, 0.3);
}

.drawer-ppt-list li:hover::before {
  opacity: 1;
}

.drawer-ppt-list li > * {
  position: relative;
  z-index: 2;
}

/* 封面图片样式 */
.ppt-cover {
  width: 120px;
  height: 120px;
  flex-shrink: 0;
  overflow: hidden;
  border-radius: 14px 0 0 14px;
  position: relative;
}

.ppt-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: all 0.3s ease;
  opacity: 0;
  animation: image-fade-in 0.5s ease forwards;
}

.ppt-cover img:hover {
  transform: scale(1.05);
}

/* 内容区域样式 */
.ppt-content {
  flex: 1;
  padding: 16px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.ppt-title {
  font-weight: 600;
  color: #f0f8ff;
  font-size: 16px;
  margin-bottom: 8px;
  line-height: 1.3;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ppt-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  font-size: 12px;
}

.ppt-date {
  color: #b0c4de;
  font-size: 12px;
  font-weight: 400;
}
.ppt-status {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-align: center;
  min-width: 60px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  align-self: flex-end;
}

.ppt-status.completed {
  background: rgba(82, 196, 26, 0.2);
  color: #52c41a;
  border: 1px solid rgba(82, 196, 26, 0.3);
}

.ppt-status.processing {
  background: rgba(24, 144, 255, 0.2);
  color: #1890ff;
  border: 1px solid rgba(24, 144, 255, 0.3);
  animation: pulse 2s infinite;
}

.ppt-status.failed {
  background: rgba(255, 77, 79, 0.2);
  color: #ff4d4f;
  border: 1px solid rgba(255, 77, 79, 0.3);
}

@keyframes pulse {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0.6;
  }
  100% {
    opacity: 1;
  }
}

/* 分页组件样式 */
.drawer-pagination {
  margin-top: 20px;
  padding: 16px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
}

.drawer-pagination :deep(.ant-pagination) {
  margin: 0;
}

.drawer-pagination :deep(.ant-pagination-item) {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 8px;
  margin: 0 2px;
  transition: all 0.3s ease;
}

.drawer-pagination :deep(.ant-pagination-item:hover) {
  background: rgba(102, 126, 234, 0.2);
  border-color: rgba(102, 126, 234, 0.4);
  transform: translateY(-1px);
}

.drawer-pagination :deep(.ant-pagination-item-active) {
  background: linear-gradient(135deg, #667eea, #764ba2);
  border-color: #667eea;
  box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}

.drawer-pagination :deep(.ant-pagination-item a) {
  color: #fff;
  font-weight: 500;
}

.drawer-pagination :deep(.ant-pagination-prev),
.drawer-pagination :deep(.ant-pagination-next) {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 8px;
  margin: 0 2px;
  transition: all 0.3s ease;
}

.drawer-pagination :deep(.ant-pagination-prev:hover),
.drawer-pagination :deep(.ant-pagination-next:hover) {
  background: rgba(102, 126, 234, 0.2);
  border-color: rgba(102, 126, 234, 0.4);
  transform: translateY(-1px);
}

.drawer-pagination :deep(.ant-pagination-prev .ant-pagination-item-link),
.drawer-pagination :deep(.ant-pagination-next .ant-pagination-item-link) {
  color: #fff;
  border: none;
  background: transparent;
}

.drawer-pagination :deep(.ant-pagination-prev .ant-pagination-item-link:hover),
.drawer-pagination :deep(.ant-pagination-next .ant-pagination-item-link:hover) {
  color: #667eea;
}
.ppt-actions {
  position: absolute;
  display: flex;
  flex-direction: column; /* 设置子元素纵向排列 */
  justify-content: flex-end;
  top: 10px;
  right: 15px;
  gap: 10px;
  margin-bottom: 6px;
}

/* 通用按钮外观（下载 / 预览） */
.downloadBtn .ant-btn,
.previewBtn .ant-btn {
  border-radius: 16px;
  padding: 0 14px;
  height: 26px;
  line-height: 24px;
  font-weight: 600;
  transition: all 0.18s ease;
  background: rgba(255, 255, 255, 0.92);
}

/* 下载按钮：红系主题 */
.downloadBtn .ant-btn {
  color: #ff4d4f;
  border: 1px solid #ff4d4f;
  box-shadow: 0 4px 10px rgba(255, 77, 79, 0.18);
}
.downloadBtn .ant-btn:hover:not([disabled]) {
  color: #fff;
  background: linear-gradient(135deg, #ff4d4f, #ff7875);
  box-shadow: 0 6px 14px rgba(255, 77, 79, 0.28);
  transform: translateY(-1px);
}

/* 预览按钮：蓝紫主题，和页面风格统一 */
.previewBtn .ant-btn {
  color: #667eea;
  border: 1px solid #667eea;
  box-shadow: 0 4px 10px rgba(102, 126, 234, 0.18);
}
.previewBtn .ant-btn:hover:not([disabled]) {
  color: #fff;
  background: linear-gradient(135deg, #667eea, #764ba2);
  box-shadow: 0 6px 14px rgba(102, 126, 234, 0.28);
  transform: translateY(-1px);
}

/* Active 与 Disabled 状态 */
.ppt-actions .ant-btn:active:not([disabled]) {
  transform: translateY(0);
  filter: brightness(0.98);
}
.ppt-actions .ant-btn[disabled] {
  cursor: not-allowed;
  opacity: 0.65;
  color: #9e9e9e !important;
  border-color: #d9d9d9 !important;
  background: rgba(255, 255, 255, 0.75) !important;
  box-shadow: none !important;
}

/* 生成进度样式 */
.progress-container {
  padding: 12px 8px 4px 8px;
}
.progress-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.progress-title {
  font-weight: 700;
  font-size: 16px;
  color: #2c3e50;
}
.progress-percentage {
  font-weight: 700;
  color: #667eea;
}
.progress-bar-container {
  margin: 10px 0 6px 0;
}
.progress-bar {
  width: 100%;
  height: 10px;
  background: #f0f0f0;
  border-radius: 8px;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  width: 0;
  background: linear-gradient(135deg, #667eea, #764ba2);
  transition: width 0.3s ease;
}
.progress-stage {
  text-align: center;
  color: #666;
  margin-top: 6px;
  font-size: 13px;
}
.progress-tips {
  margin-top: 12px;
}
.tip-item {
  display: flex;
  align-items: center;
  color: #888;
  font-size: 12px;
  margin-top: 6px;
}
.tip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #667eea;
  margin-right: 8px;
}
</style>
